<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>WebSocket Audio Transcription</title>
    <style>
        #transcriptionResult {
            white-space: pre-wrap; /* Preserve whitespace and line breaks */
            background-color: #f5f5f5;
            padding: 10px;
            border: 1px solid #ddd;
            border-radius: 5px;
            font-family: monospace;
        }
        #recordButton.recording {
            background-color: red;
            color: white;
        }
    </style>
</head>
<body>
    <div>
        <button id="recordButton">Start Recording</button>
        <label>
            language: 
            <input id="lang" type="text" value="auto" />
        </label>
        <label>
            <input type="checkbox" id="speakerVerification"> Speaker Verification
        </label>
        <hr />
        Transcription result will be displayed below:
        <p id="transcriptionResult"></p>
    </div>
</body>
<script>
    var recordButton = document.getElementById('recordButton');
    var transcriptionResult = document.getElementById('transcriptionResult');
    navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia;
    var ws = null;
    var record = null;
    var timeInte = null;
    var isRecording = false;

    recordButton.onclick = function() {
        if (!isRecording) {
            startRecording();
        } else {
            stopRecording();
        }
    };

    function startRecording() {
        console.log('Start Recording');
        var speakerVerificationCheckbox = document.getElementById('speakerVerification');
        var sv = speakerVerificationCheckbox.checked ? 1 : 0;
        var lang = document.getElementById("lang").value
        // Construct the query parameters
        var queryParams = [];
        if (lang) {
            queryParams.push(`lang=${lang}`);
        }
        if (sv) {
            queryParams.push('sv=1');
        }
        var queryString = queryParams.length > 0 ? `?${queryParams.join('&')}` : '';

        ws = new WebSocket(`wss://your_wss_server_address/ws/transcribe${queryString}`);
        ws.binaryType = 'arraybuffer';

        ws.onopen = function(event) {
            console.log('WebSocket connection established');
            record.start();
            timeInte = setInterval(function() {
                if(ws.readyState === 1) {
                    var audioBlob = record.getBlob();
                    console.log('Blob size: ', audioBlob.size);

                    // Read the Blob content for debugging
                    var reader = new FileReader();
                    reader.onloadend = function() {
                        console.log('Blob content: ', new Uint8Array(reader.result));
                        ws.send(audioBlob);
                        console.log('Sending audio data');
                        record.clear();
                    };
                    reader.readAsArrayBuffer(audioBlob);
                }
            }, 500);
        };

        ws.onmessage = function(evt) {
            console.log('Received message: ' + evt.data);
            try {
                resJson = JSON.parse(evt.data)
                if (resJson.code == 0) {
                    var jsonResponse = JSON.stringify(resJson, null, 4);
                    transcriptionResult.textContent += "\n" + (resJson.data || 'No speech recognized');
                }
            } catch (e) {
                console.error('Failed to parse response data', e);
                transcriptionResult.textContent += "\n" + evt.data;
            }
        };

        ws.onclose = function() {
            console.log('WebSocket connection closed');
        };

        ws.onerror = function(error) {
            console.error('WebSocket error: ' + error);
        };

        recordButton.textContent = "Stop Recording";
        recordButton.classList.add("recording");
        isRecording = true;
    }

    function stopRecording() {
        console.log('Stop Recording');
        if (ws) {
            ws.close();
            record.stop();
            clearInterval(timeInte);
        }
        recordButton.textContent = "Start Recording";
        recordButton.classList.remove("recording");
        isRecording = false;
    }

    function init(rec) {
        record = rec;
    }

    if (!navigator.getUserMedia) {
        alert('Your browser does not support audio input');
    } else {
        navigator.getUserMedia(
            { audio: true },
            function(mediaStream) {
                init(new Recorder(mediaStream));
            },
            function(error) {
                console.log(error);
            }
        );
    }

    var Recorder = function(stream) {
        var sampleBits = 16; // Sample bits
        var inputSampleRate = 48000; // Input sample rate
        var outputSampleRate = 16000; // Output sample rate
        var channelCount = 1; // Single channel
        var context = new AudioContext();
        var audioInput = context.createMediaStreamSource(stream);
        var recorder = context.createScriptProcessor(4096, channelCount, channelCount);
        var audioData = {
            size: 0,
            buffer: [],
            inputSampleRate: inputSampleRate,
            inputSampleBits: sampleBits,
            clear: function() {
                this.buffer = [];
                this.size = 0;
            },
            input: function(data) {
                this.buffer.push(new Float32Array(data));
                this.size += data.length;
            },
            encodePCM: function() {
                var bytes = new Float32Array(this.size);
                var offset = 0;
                for (var i = 0; i < this.buffer.length; i++) {
                    bytes.set(this.buffer[i], offset);
                    offset += this.buffer[i].length;
                }
                var dataLength = bytes.length * (sampleBits / 8);
                var buffer = new ArrayBuffer(dataLength);
                var data = new DataView(buffer);
                offset = 0;
                for (var i = 0; i < bytes.length; i++, offset += 2) {
                    var s = Math.max(-1, Math.min(1, bytes[i]));
                    data.setInt16(offset, s < 0 ? s * 0x8000 : s * 0x7FFF, true);
                }
                return new Blob([data], { type: 'audio/pcm' });
            }
        };

        this.start = function() {
            audioInput.connect(recorder);
            recorder.connect(context.destination);
        };

        this.stop = function() {
            recorder.disconnect();
        };

        this.getBlob = function() {
            return audioData.encodePCM();
        };

        this.clear = function() {
            audioData.clear();
        };

        function downsampleBuffer(buffer, inputSampleRate, outputSampleRate) {
            if (outputSampleRate === inputSampleRate) {
                return buffer;
            }
            var sampleRateRatio = inputSampleRate / outputSampleRate;
            var newLength = Math.round(buffer.length / sampleRateRatio);
            var result = new Float32Array(newLength);
            var offsetResult = 0;
            var offsetBuffer = 0;
            while (offsetResult < result.length) {
                var nextOffsetBuffer = Math.round((offsetResult + 1) * sampleRateRatio);
                var accum = 0, count = 0;
                for (var i = offsetBuffer; i < nextOffsetBuffer && i < buffer.length; i++) {
                    accum += buffer[i];
                    count++;
                }
                result[offsetResult] = accum / count;
                offsetResult++;
                offsetBuffer = nextOffsetBuffer;
            }
            return result;
        }

        recorder.onaudioprocess = function(e) {
            console.log('onaudioprocess called');
            var resampledData = downsampleBuffer(e.inputBuffer.getChannelData(0), inputSampleRate, outputSampleRate);
            audioData.input(resampledData);
        };
    };

</script>
</html>
